package indi.zhifa.engine.core.excel2025;

import cn.hutool.core.util.StrUtil;
import indi.zhifa.engine.core.common.entity.web.ServiceException;
import indi.zhifa.engine.core.common.utils.CommonUtil;
import indi.zhifa.engine.core.excel2025.annotation.ExcelColumnAnn;
import indi.zhifa.engine.core.excel2025.annotation.ExcelSheetAnn;
import indi.zhifa.engine.core.excel2025.entity.ExcelHeaderColumnInfo;
import indi.zhifa.engine.core.excel2025.entity.IRowData;
import indi.zhifa.engine.core.excel2025.entity.exception.ExcelErrorInfo;
import indi.zhifa.engine.core.excel2025.entity.exception.ExcelException;
import indi.zhifa.engine.core.excel2025.enums.EExcelErrorCode;
import indi.zhifa.engine.core.excel2025.enums.EExcelFieldType;
import indi.zhifa.engine.core.excel2025.handler.ExcelCellHandlerMemo;
import indi.zhifa.engine.core.excel2025.handler.IExcelCellHandler;
import lombok.Getter;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BaseSheetHandler<T extends IRowData> {

    private Class<T> mRowDataClass;
    private ExcelSheetAnn mExcelSheet;
    private Map<String, ExcelHeaderColumnInfo> mExcelHeaderColumnInfoMap;
    private ExcelCellHandlerMemo MExcelCellHandlerMemo;
    private List<ExcelErrorInfo>  mExcelExceptionList;

    @Getter
    protected int dataCnt;

    protected int mRowBegin = 0;
    protected int mColBegin = 0;

    public static final int MAX_COL = 100;
    public static final int MAX_ROW = 1048576;
    public static final int MAX_HEADER_ROW = 10;

    class ExcelHeaderRangInfo{
        public int rowBegin;
        public int rowEnd;
        public int colBegin;
        public int colEnd;
    }

    public BaseSheetHandler(ExcelCellHandlerMemo pExcelCellHandlerMemo) {
        mExcelHeaderColumnInfoMap =  new HashMap<String, ExcelHeaderColumnInfo>();
        mExcelExceptionList = new ArrayList<ExcelErrorInfo>();
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) superclass;
            Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
            if (actualTypeArgument instanceof Class) {
                Class<T> clazz = (Class<T>) actualTypeArgument;
                if (!clazz.isAnnotationPresent(ExcelSheetAnn.class)) {
                    throw new ServiceException("类没有加ExcelSheet注解");
                }
                ExcelSheetAnn annotation = clazz.getAnnotation(ExcelSheetAnn.class);
                Field[] fields = CommonUtil.getAllFields(clazz);
                for(Field field : fields){
                    ExcelColumnAnn excelColumn = field.getAnnotation(ExcelColumnAnn.class);
                    if(null ==  excelColumn){
                        continue;
                    }
                    String keys = String.join(",", excelColumn.keys());
                    ExcelHeaderColumnInfo excelHeaderColumnInfo = new ExcelHeaderColumnInfo();
                    excelHeaderColumnInfo.setField(field);
                    excelHeaderColumnInfo.setExcelColumn(excelColumn);

                    Class<?> valueType = null;

                    // 自动绑定
                    // 判断结构类型
                    if(excelColumn.handler() == IExcelCellHandler.class){
                        Class<?> fieldType = field.getType();
                        Type genFieldType = field.getGenericType();
                        if(fieldType.isArray()){
                            excelHeaderColumnInfo.setFieldType(EExcelFieldType.ARRAY);
                            valueType = fieldType.getClass().getComponentType();
                        }// 判断是否是 List 类型
                        else if (List.class.isAssignableFrom(fieldType)) {
                            excelHeaderColumnInfo.setFieldType(EExcelFieldType.LIST);
                            ParameterizedType paramType = (ParameterizedType) genFieldType;
                            Type[] typeArgs = paramType.getActualTypeArguments();
                            if (typeArgs.length > 0) {
                                valueType =  (Class<?>) typeArgs[0];
                            }
                        }
                        // 判断是否是 Map 类型
                        else if (Map.class.isAssignableFrom(fieldType)) {
                            excelHeaderColumnInfo.setFieldType(EExcelFieldType.MAP);
                            ParameterizedType paramType = (ParameterizedType) field.getGenericType();
                            Type[] typeArgs = paramType.getActualTypeArguments();
                            if (typeArgs.length == 2) {
                                valueType =  (Class<?>) typeArgs[1];
                            }
                        }
                        // 判断是否是单独值（基本类型或对象类型）
                        else {
                            excelHeaderColumnInfo.setFieldType(EExcelFieldType.SINGLE);
                            valueType =  fieldType;
                        }

                        // 判断字段类型
                        if(valueType.isEnum()){
                            IExcelCellHandler excelCellHandler = MExcelCellHandlerMemo.getExcelCellHandler(Enum.class);
                            excelHeaderColumnInfo.setCellHandler(excelCellHandler);
                        }else{
                            IExcelCellHandler excelCellHandler = MExcelCellHandlerMemo.getExcelCellHandler(valueType);
                            if(null == excelCellHandler){
                                throw new ServiceException("类型"+valueType.getName()+"无法自动绑定");
                            }
                            excelHeaderColumnInfo.setCellHandler(excelCellHandler);
                        }
                    }
                }
            }
        }
    }

    public boolean analysisHeader(int pRowBegin, int pColBegin, Sheet pSheet){
        List<CellRangeAddress> cellRangeAddressList = pSheet.getMergedRegions();
        // 查找表头的第一个Cell
        int col = pColBegin;
        int row = pRowBegin;

        // 查找表头起始位置

        boolean isFindBeginCell = false;
        while (row < 10) {
            if (!isBeginCell(row, col, pSheet)) {
                if (row >= col) {
                    row++;
                    col = pColBegin;
                } else {
                    col++;
                }
            } else {
                isFindBeginCell = true;
                break;
            }
        }
        if (!isFindBeginCell) {
            ExcelErrorInfo errorInfo = new ExcelErrorInfo(EExcelErrorCode.HEADER_BEGIN_CAN_NOT_FIND,
                    "sheet " + pSheet.getSheetName() + " 无法找到表头");
            errorInfo.setSheetName(pSheet.getSheetName());
            return false;
        }
        mColBegin = col;

        Cell beginCell = findCell(pSheet, row, col);
        CellRangeAddress beginHeaderCellRangAddress = findCellRangeAddress(beginCell, pSheet);
        CellRangeAddress pickTopCellAddresses = beginHeaderCellRangAddress;

        mRowBegin = beginHeaderCellRangAddress.getLastRow() + 1;
        for (int theCol = beginCell.getColumnIndex(); theCol < MAX_COL; theCol++) {

            Cell topCell = findCell(pSheet, beginHeaderCellRangAddress.getFirstRow(), theCol);
            String name = findStringCell(pSheet, topCell);
            if (null != pickTopCellAddresses) {
                if (pickTopCellAddresses.isInRange(beginHeaderCellRangAddress.getFirstRow(), theCol)) {
                    // 在首行区域内，直接OK
                } else {
                    if (!StrUtil.isEmptyIfStr(name)) {
                        break;
                    }
                }
            } else {
                if (!StrUtil.isEmptyIfStr(name)) {
                    break;
                }
            }

        }
        return false;
    }

    private ExcelHeaderRangInfo getExcelHeaderRangInfo(int pRowBegin, int pColBegin, Sheet pSheet){

        int col = pColBegin;
        int row = pRowBegin;

        // 查找表头起始位置
        ExcelHeaderRangInfo excelHeaderRangInfo = new ExcelHeaderRangInfo();

        boolean isFindBeginCell = false;
        while (row < 10) {
            if (!isBeginCell(row, col, pSheet)) {
                if (row >= col) {
                    row++;
                    col = pColBegin;
                } else {
                    col++;
                }
            } else {
                isFindBeginCell = true;
                break;
            }
        }
        if (!isFindBeginCell) {
            throw new ExcelException(EExcelErrorCode.HEADER_BEGIN_CAN_NOT_FIND,"sheet " + pSheet.getSheetName() + " 无法找到表头");
        }
        excelHeaderRangInfo.rowBegin = row;
        excelHeaderRangInfo.colBegin = col;
        // 探索表头行数
        while(row < excelHeaderRangInfo.rowBegin + MAX_HEADER_ROW){

        }
        return null;
    }


    private Cell findCell(Sheet pSheet, int pRow, int pCol) {
        Row row = pSheet.getRow(pRow);
        if (null == row) {
            return null;
        }
        return row.getCell(pCol);
    }

    private String findStringCell(Sheet pSheet, Cell pCell) {
        if (null == pCell) {
            return null;
        }
        if (pCell.getCellType() != CellType.STRING) {
            return null;
        }
        return pCell.getStringCellValue();
    }

    private boolean isBeginCell(int pRow, int pCol, Sheet pSheet) {
        Cell cell = findCell(pSheet, pRow, pCol);
        String name = findStringCell(pSheet, cell);
        if (!StrUtil.isEmptyIfStr(name)) {
            return false;
        }
        CellRangeAddress cellAddresses = findCellRangeAddress(cell, pSheet);
        return cellAddresses != null;
    }

    private CellRangeAddress findCellRangeAddress(Cell pCell, Sheet pSheet) {
        List<CellRangeAddress> cellRangeAddressList = pSheet.getMergedRegions();
        for (CellRangeAddress cellAddresses : cellRangeAddressList) {
            if (cellAddresses.isInRange(pCell)) {
                return cellAddresses;
            }
        }
        return null;
    }

}
